package cloud.hedou.abp.auth

import cloud.hedou.abp.starter.AbpContext
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.oauth2.jwt.JwtDecoder
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import javax.servlet.http.HttpServletRequest

object CurrentUser {

    private val abpJwtDecoder: JwtDecoder by lazy(AbpContext::getBean)

    /** 当前的请求 */
    val request: HttpServletRequest?
        get() {
            val requestAttributes = RequestContextHolder.getRequestAttributes() as? ServletRequestAttributes
            return requestAttributes?.request
        }

    /** 当前用户的ID */
    val id: String
        get() {
            var userId = SecurityContextHolder.getContext().authentication?.name
            if (userId.isNullOrEmpty()) {
                val jwt = abpJwtDecoder.decode(token)
                userId = jwt.subject ?: jwt.getClaim("sub")
            }
            return userId!!
        }

    /** 当前租户的ID */
    var tenantId: String
        get() = getTenantIdByToken() ?: request?.getHeader("__tenant") ?: tenantIdThreadLocal.get() ?: ""
        set(value) = tenantIdThreadLocal.set(value)

    /** 当前请求的token */
    var token: String?
        get() {
            // 优化从当前请求中获取token
            var token = request?.getHeader("authorization")
            if (token == null) {
                // 如果没有，则使用已鉴定的授权信息，一般在测试时手动设置。
                val authentication = SecurityContextHolder.getContext().authentication
                val jwtAuthenticationToken = authentication as? JwtAuthenticationToken ?: return null
                token = "Bearer " + jwtAuthenticationToken.token.tokenValue
            }
            return token
        }
        set(value) {
            val jwt = abpJwtDecoder.decode(value)
            SecurityContextHolder.getContext().authentication = JwtAuthenticationToken(jwt)
        }

    private fun getTenantIdByToken(): String? {
        return try {
            val authentication = SecurityContextHolder.getContext().authentication
            val jwtAuthenticationToken = authentication as? JwtAuthenticationToken
            val jwt = jwtAuthenticationToken?.token ?: abpJwtDecoder.decode(token)
            jwt.getClaimAsString("tenantid")
        } catch (throwable: Throwable) {
            null
        }
    }

    private val tenantIdThreadLocal = ThreadLocal<String>()

}